/*
    This file is part of libcloak.

    libcloak is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    libcloak is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with libcloak.  If not, see <http://www.gnu.org/licenses/>.

*/



#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include "../include/Manager.h"
#include <pthread.h>
#include <signal.h>
#include <unistd.h>





namespace Cloak
{
	
	using namespace std;
	using namespace boost;
	
	Manager::Exception::Exception(int value, string message): value(value),
						       message(message)
	{}
	
	
	Manager::Manager()
	{
		root = shared_ptr<Node>(new Node());
		root->name = "*";
		
		max_cache_size = MAX_CACHE_SIZE;
		
		pthread_mutex_init(&prefixMutex, NULL);
		pthread_mutex_init(&cacheMutex, NULL);
		pthread_mutex_init(&treeMutex, NULL);
		pthread_mutex_init(&getEntMutex, NULL);
	}
	
	Manager::~Manager()
	{
		stopGarbageThreads();
		
		pthread_mutex_destroy(&prefixMutex);
		pthread_mutex_destroy(&cacheMutex);
		pthread_mutex_destroy(&treeMutex);
		pthread_mutex_destroy(&getEntMutex);
		
		pair<double, pair<string,void*> > n;
		BOOST_FOREACH(n, cacheLRU)
		{
			delete reinterpret_cast<weak_ptr<int>* >(n.second.second);
		}
		
	}
	
	PathVector Manager::vectorizePath(const string &path)
	{
		PathVector ret;
		
		// Use Boost::Tokenize to tokenize the string.
		char_separator<char> sep("/"); 
		tokenizer<char_separator<char> > tok(path, sep);
		
		bool foundStar = false;
		
		// Push all into vector
		for(tokenizer<char_separator<char> >::iterator it = tok.begin();
		    it != tok.end(); ++it)
		{
			
			if(foundStar)
			{
				throw Exception(Exception::BADPATH, "BADPATH vectorizePath/1: Path has a star that is not at the end");
			}
			
			if(*it == "*")
				foundStar = true;
			ret.push_back(*it);
		}
		
		return ret;
		
	}
	
	void Manager::parseAttrs(const string &attrs, AttrMap &amap)
	{		
		/* For now, rolling my own parser for an attribute map 
		   with the following grammar:
		   
		   STR: std::string | STR\:STR | STR\,STR | STR\\STR | empty_string
		   ATTR: STR:STR
		   ATTRLIST: ATTR,ATTRLIST | ATTR

		   These things look like:
		   name:chris,age:21,location:United States,path:/usr/bin\:/home/bin

		   It is a comma separated list of pairs of strings.
		   Each element in the pair is separated by a colon.
		   Backslashes escape those special chars in values.
		*/
		
		bool gettingName = true;
		string name = "";
		string value = "";
		for(unsigned int i=0; i < attrs.length(); i++)
		{
			char ch = attrs[i];
			if(ch == '\\')
			{
				if(i >= attrs.length() - 1)
					throw Exception(Exception::BADATTR, "BADATTR parseAttrs/2: Attribute syntax is bad, ending with escape character");
				
				i++;
				char es = attrs[i];
				if(es != ':' && es != ',' && es != '\\')
				{
					throw Exception(Exception::BADATTR, "BADATTR parseAttrs/2: Attribute syntax is bad, unknown escape character");
				}
				else
				{
					if(gettingName)
						name += es;
					else
						value += es;
				}
			}
			else if(ch == ':')
			{
				if(!gettingName)
					throw Exception(Exception::BADATTR, "BADATTR parseAttrs/2: Attribute syntax is bad, unescaped ':' in value");
				else 
					gettingName = false;
			}
			else if(ch == ',')
			{
				if(gettingName)
					throw Exception(Exception::BADATTR, "BADATTR parseAttrs/2: Attribute syntax is bad, no value given before ','");
				else
				{
					amap[name] = value;
					name = "";
					value = "";
					gettingName = true;
				}
			}
			else
			{
				if(gettingName)
					name += ch;
				else
					value += ch;
			}
		}
		
		// Get last one
		if(name != "")
			amap[name] = value;
				
	}

	shared_ptr<Handler> Manager::getHandler(const string &path)
	{
		PathVector vec = vectorizePath(path);
		
		/*
		// Exception if path is empty
		if(vec.size() == 0)
		{
			throw Exception(Exception::NOPATH, "NOPATH getHandler/1: Path empty");
		}
		*/
		
		// Find the next handler
		ChildrenMap::iterator nextit = root->children.find(vec.front());
		
		// If it cannot be found, return the default handler.
		if(nextit == root->children.end())
		{
			if(root->handler != NULL)
				return root->handler;
			else
				throw Exception(Exception::NODEF, "NODEF getHandler/1: No default handler");
		}
		// Otherwise, go down the tree to look for a more specific handler
		else
		{
			PathVector::iterator pathit = vec.begin();
			++pathit; // Increment to second element.
			
			
			// Call the other getHandler method.
			// Pass it:
			// Next subtree root
			// Most specific handler we know about
			// Iterator to head of path subvector
			// End of the path vector for comparisons
			try
			{
				return this->getHandler(nextit->second, 
							root->handler,
							root->handler,
							pathit, vec.end());
			}
			catch(Manager::Exception e)
			{
				int val = e.getValue();
				string msg = e.getMessage();
				msg += " for path <" + path + ">";
				
				throw Exception(val, msg);
			}
		}	
		
		
	}
	
	shared_ptr<Handler> Manager::getHandler(shared_ptr<Node> subroot,
			shared_ptr<Handler> best,
			shared_ptr<Handler> bestwild,
			PathVector::iterator &current,
			PathVector::iterator end)
	{
		// If our wildcard handler for this subtree is not null, 
		// that is the best one for now
		if(subroot->handler != NULL)
		{
			
			best = subroot->handler;
			
			// If this is not a leaf node, this handler is 
			// the best ONLY wildcard handler found.
			if(subroot->handler->isWild())
			{
				bestwild = subroot->handler;
			}
		}
		
		// If we have reached the end of the path, the best so far
		// is the best, regardless of whether or not it is a wild card
		// match
		if(current == end)
		{
			if(best != NULL)
				return best;
			else
				throw Exception(Exception::NOTFOUND, "NOTFOUND getHandler/3: Could not find a handler");
		}
		
		// Find an entry for the next part of the path
		ChildrenMap::iterator next = subroot->children.find(*current);
		
		// If we can't find one, return the best wildcard we have so far.
		if(next == subroot->children.end())
		{
			if(bestwild != NULL)
				return bestwild;
			else
				throw Exception(Exception::NOTFOUNDWILD, "NOTFOUNDWILD getHandler/3: Could not find a wildcard handler");
		}
		// Otherwise, keep going down the tree to find a more specific
		// handler
		else
		{
			return getHandler(next->second, best, bestwild, ++current, end);
		}
	}
	
	void Manager::garbageCollect(GarbageMode mode)
	{
		pthread_mutex_lock(&cacheMutex);
		
		if(mode == FULL)
		{
			map<double, pair<string,void*> >::iterator it;
			for(it = cacheLRU.begin(); it != cacheLRU.end(); ++it)
			{
				
				weak_ptr<int> *temp =  reinterpret_cast<weak_ptr<int>* >(it->second.second);
				if(temp->use_count() == 1)
				{
					cache.erase(it->second.first);
					delete temp;
					cacheLRU.erase(it);
				}
			}
		}
		else if(mode == QUARTER_SPACE)
		{
			int original = cacheLRU.size();
			int checked = 0;
			
			map<double, pair<string,void*> >::iterator it;
			for(it = cacheLRU.begin(); it != cacheLRU.end(); ++it)
			{
				// Leave after we have checked 1/4 of them
				if(original/4 <= checked++)
					break;
				
				weak_ptr<int> *temp =  reinterpret_cast<weak_ptr<int>* >(it->second.second);
				if(temp->use_count() == 1)
				{
					cache.erase(it->second.first);
					delete temp;
					cacheLRU.erase(it);
				}
			}
		}
		else if(mode == QUARTER_SLIDING)
		{
			int original = cacheLRU.size();
			int deleted = 0;
			
			map<double, pair<string,void*> >::iterator it;
			for(it = cacheLRU.begin(); it != cacheLRU.end(); ++it)
			{
				// Leave after we have deleted 1/4 of them
				if(original/4 <= deleted)
					break;
				
				weak_ptr<int> *temp =  reinterpret_cast<weak_ptr<int>* >(it->second.second);
				if(temp->use_count() == 1)
				{
					cache.erase(it->second.first);
					delete temp;
					cacheLRU.erase(it);
					
					++deleted;
				}
			}
		}
		
		pthread_mutex_unlock(&cacheMutex);
	}
	
	void Manager::startGarbageThread(GarbageMode mode, int time)
	{
		garbage_thread_data_t *data = new garbage_thread_data_t;
		data->man = this;
		data->mode = mode;
		data->time = time;
		pthread_t *thread = new pthread_t;
		pthread_create(thread, NULL, Manager::garbage_thread, data);
		garbageThreads.push_back(thread);
	}
	
	void Manager::stopGarbageThreads()
	{
		// Lock cache mutex to make sure that no garbage thread is
		// doing anything at the moment
		pthread_mutex_lock(&cacheMutex);
		
		// For each thread, cancel it, wait for it to be canceled,
		// then erase it from the thread list.
		for(vector<pthread_t*>::iterator it = garbageThreads.begin();
			it != garbageThreads.end();)
		{
			pthread_cancel(**it);
			void *trash;
			pthread_join(**it, &trash);
			delete *it;
			it = garbageThreads.erase(it);
		}
		
		pthread_mutex_unlock(&cacheMutex);
	}
	
	void* Manager::garbage_thread(void *data)
	{
		// Get all of the data that was passed in and delete
		// what we were given, everything is on the stack now.
		garbage_thread_data_t *gtdata = (garbage_thread_data_t*) data;
		Manager *manager = gtdata->man;
		GarbageMode mode = gtdata->mode;
		int time = gtdata->time;
		delete gtdata;
		
		for(;;)
		{
			manager->garbageCollect(mode);
			
			
			usleep(time*1000);
		}
		
		return NULL;
	}
	
	void Manager::addHandler(const string &str, Handler *handler)
	{
		// Make pointer to the handler
		shared_ptr<Handler> ptr(handler);
		
		// Vectorize path
		PathVector path = vectorizePath(str);
		
		
		// Done exception-happy code. Lock treeMutex now.
		lock_mutex(&treeMutex);
		
		// If the front of the path is the wildcard this is default
		if(path.front() == "*")
		{
			root->handler = ptr;
			unlock_mutex(&treeMutex);
			return;
		}
		
		// If the back of the path is the wildcard, it should be removed
		// because the handler is a wildcard
		if(path.back() == "*")
		{
			path.pop_back();
		}
		
		
		shared_ptr<Node> current = root;
		ChildrenMap::iterator next;
		
		// Go down the tree and make new nodes down to the correct level
		for(PathVector::iterator it = path.begin(); it != path.end(); ++it)
		{
			// Find this part
			next = current->children.find(*it);
			
			// If not found make it, then find reference 
			if(next == current->children.end())
			{
				shared_ptr<Node> n(new Node());
				n->name = *it;
				current->children.insert(make_pair(*it, n));
				next = current->children.find(*it);
			}                                  
			
			// Set current to next node
			current = next->second;
		}
		
		// Copy new handler to tree.
		current->handler = ptr;
		
		// Done adding, unlock.
		unlock_mutex(&treeMutex);
	}
	
	void Manager::debugTree()
	{
		lock_mutex(&treeMutex);
		
		debugTree(root, 0);
		
		unlock_mutex(&treeMutex);
	}
	
	void Manager::debugTree(shared_ptr<Node> current, int depth)
	{
		// Output spaces to correct depth
		for(int i=0; i<depth; i++)
			cerr<<"  ";
		
		cerr << current->name << " (" << current->handler << ")" << 
			((current->handler->isWild())?(" [W]"):("")) << endl;
		
		pair<string, shared_ptr<Node> > n;
		BOOST_FOREACH(n, current->children)
		{
			debugTree(n.second, depth+1);
		}
	}
	
	void Manager::addPrefix(const string &prefix, const string &path)
	{
		lock_mutex(&prefixMutex);
		
		prefixMap.insert(make_pair(prefix,path));
		
		unlock_mutex(&prefixMutex);
	}
	
	string Manager::getPrefix(const string &prefix)
	{
		lock_mutex(&prefixMutex);
		
		map<string,string>::iterator it = prefixMap.find(prefix);
		
		if(it == prefixMap.end())
		{
			unlock_mutex(&prefixMutex);
			
			return "";
		}
		else
		{
			string ret = it->second;
			
			unlock_mutex(&prefixMutex);
			
			return ret;
		}
	}
	
	void Manager::lock_mutex(pthread_mutex_t *mutex)
	{
		#ifndef NOTHREADS
		pthread_mutex_lock(mutex);
		#endif
	}
	
	
	void Manager::unlock_mutex(pthread_mutex_t *mutex)
	{
		#ifndef NOTHREADS
		pthread_mutex_unlock(mutex);
		#endif
	}
	
	void Manager::debugCache()
	{
		lock_mutex(&cacheMutex);
		
		cerr << "Cache LRU list (" << cacheLRU.size() << " entries)\n";
		pair<double, pair<string,void*> > n;
		int i = 1;
		BOOST_FOREACH(n, cacheLRU)
		{
			cerr << i <<": " << n.first << " - " 
				<< n.second.first << " ["<< 
				reinterpret_cast<weak_ptr<int>*>(n.second.second)->use_count()
				<< "]" << endl;
			i++;
		}
		
		unlock_mutex(&cacheMutex);
	}
	
}
